package com.bss;

import java.util.List;

import javax.management.RuntimeErrorException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

public abstract class Dao<T extends Entity> {

	private static final Log log = LogFactory.getLog(Dao.class);

	private Utils utils;

	@PersistenceContext
	private EntityManager entityManager;

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	public EntityManager getEntityManager() {
		return this.entityManager;
	}

	@Transactional
	public void persist(final T transientInstance) {
		Dao.log.debug("persisting " + transientInstance.getClass().getName()
				+ " instance");

		try {
			entityManager.getTransaction().begin();

			getEntityManager().persist(transientInstance);
			getEntityManager().flush();
			Dao.log.debug("persist successful");

			entityManager.getTransaction().commit();

		} catch (RuntimeException re) {
			Dao.log.error("persist failed", re);

			throw re;
		}

	}

	@Transactional
	public void remove(final T persistentInstance) {
		Dao.log.debug("removing " + persistentInstance.getClass().getName()
				+ " instance");
		try {
			getEntityManager().remove(persistentInstance);
			Dao.log.debug("remove successful");
		} catch (RuntimeException re) {
			try {
				removeEntity(persistentInstance);
			} catch (RuntimeErrorException ee) {
				Dao.log.error("remove failed", re);
				throw re;
			}
		}
	}

	@Transactional
	public void removeEntity(final Entity entity) {
		removeById(entity.getId());
	}

	@Transactional
	public void removeById(final Integer id) {
		remove(findById(id));
	}

	@Transactional
	public T merge(final T detachedInstance) {
		Dao.log.debug("merging " + detachedInstance.getClass().getName()
				+ " instance");
		try {
			T result = getEntityManager().merge(detachedInstance);
			Dao.log.debug("merge successful");
			return result;
		} catch (RuntimeException re) {
			Dao.log.error("merge failed", re);
			throw re;
		}
	}

	@Transactional
	public void update(final T t) {
		merge(t);
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll() {

		String model = getModelClass().getName();
		Dao.log.trace("getting all " + model);
		try {
			return getEntityManager().createQuery("from " + model)
					.getResultList();
		} catch (RuntimeException re) {
			Dao.log.error("get failed", re);
			throw re;
		}
	}

	public List<T> getAll() {
		return findAll();
	}

	public T findById(final Integer id) {
		Dao.log.debug("getting " + getModelClass().getName()
				+ " instance with id: " + id);
		try {
			return getEntityManager().find(getModelClass(), id);
		} catch (RuntimeException re) {
			Dao.log.error("get failed", re);
			throw re;
		}
	}

	public List<T> find(final String condition) {
		@SuppressWarnings("unchecked")
		List<T> list = this.entityManager.createQuery(
				"from " + getModelClass().getName() + " where " + condition)
				.getResultList();
		return list;
	}

	public List<T> findWithAggregateFunction(final String function,
			final String column) {

		Query query = this.entityManager.createQuery("from "
				+ getModelClass().getName() + " where " + column
				+ " in ( Select " + function + "(" + column + ") from "
				+ getModelClass().getName() + " )");

		@SuppressWarnings("unchecked")
		List<T> list = query.getResultList();

		return list;
	}

	public List<T> findWithParameters(final String condition,
			final Object... parameterValues) {

		Query query = this.entityManager.createQuery("from "
				+ getModelClass().getName() + " where " + condition);

		for (int position = 1; position <= parameterValues.length; position++) {
			query.setParameter(position, parameterValues[position - 1]);
		}

		@SuppressWarnings("unchecked")
		List<T> list = query.getResultList();

		return list;
	}

	public List<T> findComplex(final String condition) {
		@SuppressWarnings("unchecked")
		List<T> list = this.entityManager.createQuery(condition)
				.getResultList();
		return list;
	}

	@SuppressWarnings("unchecked")
	protected Class<T> getModelClass() {
		try {
			return (Class<T>) Class.forName(getModelClassName());
		} catch (ClassNotFoundException e) {
			Dao.log.debug(e.getMessage());
		}
		return null;
	}

	protected String getModelClassName() {
		String name = this.getClass().getName();
		return name.substring(0, name.indexOf("Dao")).replace("dao", "model");
	}

	public void refresh(final T entity) {
		this.entityManager.refresh(entity);
	}

	@Autowired
	public void setUtils(final Utils utils) {
		this.utils = utils;
	}

	public Utils getUtils() {
		return this.utils;
	}

	

}
